#!/usr/bin/perl -w

use strict;
use constant PRODUCT => '[auto-ctm] Auto CTM v1.5';
use constant SITE    => 'ftp://ftp.freebsd.org/pub/FreeBSD/development/CTM/ports-cur/';
use constant CTM     => '/usr/sbin/ctm -F';
use constant WGET    => '/usr/local/bin/wget';
use constant FETCH   => '/usr/bin/fetch';
use constant REP     => WGET . ' -q -O - ' . SITE;
use constant DIR     => '/usr/ports';
use constant TEMP    => 'ctm_cur';
use constant STATUS  => DIR . '/.ctm_status';

use constant INDEX => $ARGV[0] && $ARGV[0] eq "fetch" ? 'fetchindex' : 'index';

use vars qw /$NUM/;

main();

exit (0);

sub main {
	print PRODUCT . "\n";

	select ((select(STDOUT),$|=1)[0]);
	chdir(DIR) || die "Cannot chdir to ".DIR." $!\n";
	$ARGV[0] ||= '';

	my $num = _read_status();

	my $stat = ($ARGV[0] eq 'root') ? _create_root() : _get_files($num);

	if ($stat > 0) {
		system "make " . INDEX;
	} else {
		print "Port collection is current\n";
	}
}

sub _create_root {
	if (opendir DDIR, DIR) {
		my @f = grep !/\.gz$/ && !/^\./, readdir(DDIR);
		closedir DDIR;
		if (scalar @f) {
			system "rm -rf " . DIR if (-d DIR);
			mkdir (DIR, 0755);
		}
		return _get_empty();
	}
	return 0;
}

sub _get_empty {
	my $file = (_read_list(0,qr/^[^\"]+\"(.*?ports-cur\.(\d+)xEmpty\.gz)\".*$/))[-1];
	if ($file) {
		chomp $file;
		$file =~ /\/([^\/]+)$/;
		my $name = DIR . '/' . TEMP . "/$1";
		
		if (-s $name) {
			print "WARNING: $name exists! Trying to install...\n";
		} else {
			print "Downloading $name...\n";
			system FETCH . " -o $name -q $file";
			if (-z $name) {
				system WGET . " -q $file -O $name";
			}
		}
		return _install($name);
	}
	return 0;
}

sub _get_files {
	my $num = shift;

	my $rm_old = sprintf qq{rm -f %s/%s/ports-cur.%d\*},
		DIR, TEMP, substr($num,0,-2) -1 ;
	system "$rm_old";

	my $wget = sprintf qq{%s -q -nc -P %s/%s %s/ports-cur.%s\\\?\\\?.gz},
		WGET, DIR, TEMP, SITE, substr($num,0,-2);

	print "Retrieving delta files...";
	system $wget;
	print "done\n";

	my @files = 
		map { /\/(ports-cur\.\d+\.gz)$/; DIR . '/' . TEMP . '/' . $1 }
		_read_list($num,qr/^[^\"]+\"(.*?ports-cur\.(\d+)\.gz)\".*$/);

	my $total = 0;
	foreach (@files) {
		$total += _install($_);
	}
	return $total;
}

sub _install {
	my $name = shift;
	if (-s $name) {
		if (open (PIPE, CTM . " $name 2>&1 |")) {
			my $cp = my $cd = '';
			while (<PIPE>) {
				chomp;
				my ($gt,$p,$d) = (split /[\s\/]/, $_, 5)[0,2,3];
				next unless $gt && $p && $d && $d ne 'Makefile';
				if ($gt eq ">" && $p ne $cp && $p ne $cd) {
					printf " + %-60s\r", substr("$p | $d",-60);
					$cp = $p;
					$cd = $d;
				}
			}
			close PIPE;
			printf " + %-60s DONE\n", $name;
			return 1;
		} else {
			print "ERROR: executing " . CTM . " ($!) \n";
		}
	} else {
		print "ERROR: Downloaded $name failed!\n";
	}
	return 0;
}

sub _read_list {
	my ($num,$re) = (@_);
	my @list = ();
	if (open PIPE, REP . " 2>&1 |") {
		while (<PIPE>) {
			next unless s/$re/$1/;
			next if $2 <= $num;
			push @list, $_;
		}
		close PIPE;
	} else {
		print "Cannot open pipe: $!\n";
	}
	return @list;
}

sub _read_status {
	my $num = 0;
	if (open F, STATUS) {
		$num = <F>;
		close F;
		$num =~ s/^\D+(\d+).*\n$/$1/;
	} else {
		$ARGV[0] = "root";
	}
	return $num;
}

1;

__END__

=head1 NAME

B<auto-ctm> - keep ports collection up-to-date.

=head1 SYNOPSIS

./auto-ctm

=head1 DESCRIPTION

B<auto-ctm> uses C<ctm> to keep ports up-to-date by checking the site:

	ftp://ftp.freebsd.org/pub/FreeBSD/development/CTM/ports-cur/

If ports number is greater than the current port which is in 
F</usr/ports/.ctm_status>, it will download the port(s) and install it with C<-F>.

=head2 Parameters

=over 4

=item fetch

Causes the program to execute C<make fetchindex> instead of C<make index>. You may remove the compressed file if you wish.

=item root

Recreate the entire source tree from the latest distribution.

=back

=head2 Constants

=over 4

=item CTM

Points to the C<ctm> program.

=item WGET

Points to the C<wget> program.

=item REP

Repository. C<WGET> and arguments and C<SITE>

=item SITE

Location where to retrieve the ports.

=item DIR

Usually F</usr/ports>.

=item STATUS

Status file C<DIR . '/.ctm_status'>.

=back

=head2 Main Program

=head3 Get version

Opens C<STATUS> and get the current port number, this will be used to check for
current version of the port in comparison to the ones on C<SITE>.

=head3 Retrieves new ports

Opens a pipe to the C<REP> and retrieves the port list. Generate a list of new
ports to be use to retrieve the actual ports.

If the list is empty, the port returns:

	Port collection is current

Otherwise, it executes C<_get_file> per port.

=head2 _get_file (F<path>)

Get file connects to C<REP> and retrieves the port to update. 
It retrieves the C<.gz> file.

Opens the pipe to C<CTM> and install the port.

=head1 HISTORY

=over 4

=item v1.0

20050415 - Created.

=item v1.1

20050509 - Add POD. Rearrange constants for absolute paths.

=item v1.2

20050511 - Change to DIR.

=item v1.3

20050512 - make index

=item v1.4

20051117 - added "root" option to create the root tree.
consolidate the output to make it more readable.

=item v1.41

20051118 - make the output a little prettier.

=item v1.5

20090819 - batch download unstead of fetching individual files.

=back

=head1 AUTHOR

This program by Paul Pham <paul@aquaron.com>.

=cut
